import * as THREE from "three"
import { Octree } from 'three/addons/math/Octree.js';
import { OctreeHelper } from 'three/examples/jsm/helpers/OctreeHelper.js';
import { Capsule } from 'three/addons/math/Capsule.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { gui } from "../system/gui"
import CapsuleHelper from "./CapsuleHelper"
interface spheresType {
    collider: THREE.BufferGeometry;
    velocity: THREE.Vector3;
    mesh: THREE.Mesh;
}
const GRAVITY = 30;
const NUM_SPHERES = 3; // 最大小球数量，超过该值将回收释放旧的小球
const SPHERE_RADIUS = 0.2;
const vector1 = new THREE.Vector3();
const vector2 = new THREE.Vector3();
const vector3 = new THREE.Vector3();
const sphereGeometry = new THREE.IcosahedronGeometry(SPHERE_RADIUS, 5);
const sphereMaterial = new THREE.MeshLambertMaterial({ color: 0xdede8d });
let spheres: spheresType[] = []
let sphereIdx = 0;
let that: any
let obj = {
    look: false,
}

// 八叉树，碰撞检测
const worldOctree = new Octree();
// 角色碰撞胶囊
const playerCollider = new Capsule(new THREE.Vector3(0, 0.35, 0), new THREE.Vector3(0, 1.5, 0), 0.35);
const playerColliderHelper = CapsuleHelper(0.6, 1.5)
// 修正角色位置使其触地
const playerFixVector = new THREE.Vector3(0, 0.35, 0)
// 运动速度
const playerVelocity = new THREE.Vector3();
// 运动方向
const playerDirection = new THREE.Vector3();
// 角色是否在地面
let playerOnFloor = false;

let mouseTime = 0;
// 按下前进按键的持续时间
let ForwardHoldTimeClock = new THREE.Clock()
ForwardHoldTimeClock.autoStart = false

// 视角拖动灵敏度
let cameraMoveSensitivity = 0.4
gui.add({ cameraMoveSensitivity: cameraMoveSensitivity }, "cameraMoveSensitivity").name("视角拖动灵敏度延迟").step(0.1).min(0).max(1)
    .onChange(function (value) {
        cameraMoveSensitivity = value;
    });
// 按键事件状态
const keyStates = {
    // 使用W、A、S、D按键来控制前、后、左、右运动
    // false表示没有按下，true表示按下状态
    W: false,
    A: false,
    S: false,
    D: false,
    ShiftLeft: false,
    Space: false,
    leftMouseBtn: false,
};
// 角色运动状态
const playerActionState = {
    forward: 0,
    turn: 0
}
// 投掷小球
function throwBall() {
    const sphere = spheres[sphereIdx]
    // 将相机视角的方向作为角色运动方向
    that.player.getWorldDirection(playerDirection);
    // @ts-ignore
    sphere.collider.center.copy(playerCollider.end).addScaledVector(playerDirection, playerCollider.radius * 1.5);
    const impulse = 15 + 30 * (1 - Math.exp((mouseTime - performance.now()) * 0.001));

    sphere.velocity.copy(playerDirection).multiplyScalar(impulse);
    sphere.velocity.addScaledVector(playerVelocity, 2);

    sphereIdx = (sphereIdx + 1) % spheres.length;
}
// 角色碰撞检测
function playerCollisions() {
    const result = worldOctree.capsuleIntersect(playerCollider);
    playerOnFloor = false;
    if (result) {
        playerOnFloor = result.normal.y > 0;
        if (!playerOnFloor) {
            playerVelocity.addScaledVector(result.normal, - result.normal.dot(playerVelocity));
        }
        playerCollider.translate(result.normal.multiplyScalar(result.depth));
    }
}
// 更新角色位置信息
function updatePlayer(deltaTime: number) {
    if (!(that.player instanceof THREE.Object3D)) return

    let speedRatio = 1.5
    let damping = Math.exp(- 20 * deltaTime) - 1; // 阻尼减速

    if (!playerOnFloor) {
        playerVelocity.y -= GRAVITY * deltaTime;
        damping *= 0.1;
        speedRatio = 2
    }

    playerVelocity.addScaledVector(playerVelocity, damping);

    // 前进
    if (playerActionState.forward > 0) {
        if (playerActionState.turn != 0) {
            that.player.rotation.y -= playerActionState.turn * deltaTime * 2
        }
        // 前进状态持续2s以上转为跑步状态
        if (ForwardHoldTimeClock.getElapsedTime() > 2 || (keyStates['ShiftLeft'] && keyStates["W"])) {
            if (playerOnFloor) speedRatio = 5
            changeAction('Standard Run')
        } else {
            changeAction('Walking')
        }
    }
    if (playerActionState.forward < 0) {
        changeAction('Walking')
    }
    // 原地转向
    if (playerActionState.forward == 0 && playerActionState.turn != 0) {
        changeAction('Walking')
        that.player.rotation.y -= playerActionState.turn * deltaTime * 2
    }
    // 休息状态
    if (playerActionState.forward == 0 && playerActionState.turn == 0) {
        changeAction('Breathing Idle')
    }

    const deltaPosition = playerVelocity.clone().multiplyScalar(deltaTime * speedRatio); // 速度*时间 = 移动距离 (向量)
    deltaPosition.y /= speedRatio // 速度系数不对高度分量产生效果
    playerCollider.translate(deltaPosition);

    playerCollisions();

    that.player.position.copy(new THREE.Vector3().subVectors(playerCollider.start, playerFixVector)); // 更新角色位置，辅以修正向量角色触地

}
// 玩家和球的碰撞检测
function playerSphereCollision(sphere: any) {
    const center = vector1.addVectors(playerCollider.start, playerCollider.end).multiplyScalar(0.5);
    const sphere_center = sphere.collider.center;
    const r = playerCollider.radius + sphere.collider.radius;
    const r2 = r * r;
    for (const point of [playerCollider.start, playerCollider.end, center]) {
        const d2 = point.distanceToSquared(sphere_center);
        if (d2 < r2) {
            const normal = vector1.subVectors(point, sphere_center).normalize();
            const v1 = vector2.copy(normal).multiplyScalar(normal.dot(playerVelocity));
            const v2 = vector3.copy(normal).multiplyScalar(normal.dot(sphere.velocity));
            playerVelocity.add(v2).sub(v1);
            sphere.velocity.add(v1).sub(v2);
            const d = (r - Math.sqrt(d2)) / 2;
            sphere_center.addScaledVector(normal, - d);
            console.log(sphere_center);
        }
    }
}
// 球体和球体碰撞检测
function spheresCollisions() {
    for (let i = 0, length = spheres.length; i < length; i++) {
        const s1: any = spheres[i];
        for (let j = i + 1; j < length; j++) {
            const s2: any = spheres[j];
            const d2 = s1.collider.center.distanceToSquared(s2.collider.center);
            const r = s1.collider.radius + s2.collider.radius;
            const r2 = r * r;
            if (d2 < r2) {
                const normal = vector1.subVectors(s1.collider.center, s2.collider.center).normalize();
                const v1 = vector2.copy(normal).multiplyScalar(normal.dot(s1.velocity));
                const v2 = vector3.copy(normal).multiplyScalar(normal.dot(s2.velocity));
                s1.velocity.add(v2).sub(v1);
                s2.velocity.add(v1).sub(v2);
                const d = (r - Math.sqrt(d2)) / 2;
                s1.collider.center.addScaledVector(normal, d);
                s2.collider.center.addScaledVector(normal, - d);
            }
        }
    }
}

function updateSpheres(deltaTime: number) {

    spheres.forEach((sphere: any) => {

        sphere.collider.center.addScaledVector(sphere.velocity, deltaTime);

        const result = worldOctree.sphereIntersect(sphere.collider);

        if (result) {

            sphere.velocity.addScaledVector(result.normal, - result.normal.dot(sphere.velocity) * 1.5);
            sphere.collider.center.add(result.normal.multiplyScalar(result.depth));

        } else {

            sphere.velocity.y -= GRAVITY * deltaTime;

        }

        const damping = Math.exp(- 1.5 * deltaTime) - 1;
        sphere.velocity.addScaledVector(sphere.velocity, damping);

        playerSphereCollision(sphere);

    });
    spheresCollisions();

    for (const sphere of spheres) {

        sphere.mesh.position.copy(sphere.collider.center as unknown as THREE.Vector3);

    }

}
// 前进方向上的向量
function getForwardVector() {
    that.player.getWorldDirection(playerDirection);
    playerDirection.y = 0;
    playerDirection.normalize();
    return playerDirection;

}
// 横移方向上的向量
function getSideVector() {
    that.player.getWorldDirection(playerDirection);
    playerDirection.y = 0;
    playerDirection.normalize();
    playerDirection.cross(that.player.up);
    return playerDirection;

}

// 角色控制
function controls(deltaTime: number) {
    const speedDelta = deltaTime * (playerOnFloor ? 25 : 8);
    if (keyStates['W']) {
        playerVelocity.add(getForwardVector().multiplyScalar(speedDelta));
    }
    if (keyStates['S']) {
        playerVelocity.add(getForwardVector().multiplyScalar(-speedDelta));
    }
    if (keyStates['A']) {
        playerVelocity.add(getSideVector().multiplyScalar(-speedDelta));
    }
    if (keyStates['D']) {
        playerVelocity.add(getSideVector().multiplyScalar(speedDelta));
    }
    if (playerOnFloor) {
        if (keyStates['Space']) { playerVelocity.y = 15; }
    }
}

function teleportPlayerIfOob() {
    if (!(that.player instanceof THREE.Object3D)) return
    if (that.player.position.y <= - 25) {
        playerCollider.start.set(0, 0.35, 0);
        playerCollider.end.set(0, 1, 0);
        playerCollider.radius = 0.35;
        that.player.position.copy(new THREE.Vector3().subVectors(playerCollider.start, playerFixVector));
        that.player.rotation.set(0, 0, 0);
    }
}
/**
 * 切换动作
 * @param {string} actionName 动画名称
 */
function changeAction(actionName: string) {
    if (that.allActions[actionName] && that.currentAction.name != actionName) {
        if (that.currentAction.name === 'Breathing Idle') {
            executeCrossFade(actionName);
        } else {
            executeCrossFade(actionName);
        }
    }
}
function executeCrossFade(actionName: string) {
    // that.currentAction.weight = 0    // 停止当前动画，设置权重为0
    const action = that.allActions[actionName]
    // action.weight = 1    // 播放当前动画
    action.enabled = true;
    action.setEffectiveTimeScale(1);
    action.setEffectiveWeight(1);
    action.time = 0
    that.currentAction.crossFadeTo(action, 0.35, true);
    that.currentAction = action
}
// debugObject.animations = {
//     Standard Run:()=>{
//         changeAction('Standard Run')
//     },
//     Walking:()=>{
//         changeAction('Walking')
//     },
//     Breathing Idle:()=>{
//         changeAction('Breathing Idle')
//     }
// }
// for(let obj in debugObject.animations){
//     gui.add(debugObject.animations,obj)
// }
export function gameUpdate(deltaTime: number) {
    // 控制
    controls(deltaTime);
    // 更新位置
    updatePlayer(deltaTime);
    updateSpheres(deltaTime);
    // 复位检测
    teleportPlayerIfOob();
}

/**
 * 
 * @param {ThreeApp} th 
 */
export function gameInit(th: any) {
    that = th
    // 小球
    for (let i = 0; i < NUM_SPHERES; i++) {
        const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
        sphere.castShadow = true;
        sphere.receiveShadow = true;
        that.scene.add(sphere);
        spheres.push({
            mesh: sphere,
            // @ts-ignore
            collider: new THREE.Sphere(new THREE.Vector3(0, - 100, 0), SPHERE_RADIUS),
            velocity: new THREE.Vector3()
        });

    }
    let keyW = false
    // 当某个键盘按下设置对应属性设置为true
    document.addEventListener('keydown', (event: KeyboardEvent) => {
        if (event.code === 'KeyW') {
            keyStates.W = true; playerActionState.forward = 1;
            if (!keyW) { // keydown事件在按下按键不松时会持续激活，因此需进行状态控制，避免计时器重复计时
                ForwardHoldTimeClock.start()
                keyW = true
                keyStates.ShiftLeft = false;
            }
        }
        // 同时按下shift键，则速度翻倍
        if (event.code === 'ShiftLeft') {
            keyStates.ShiftLeft = true;
        }
        if (event.code === 'KeyA') { keyStates.A = true; playerActionState.turn = -1 }
        if (event.code === 'KeyS') { keyStates.S = true; playerActionState.forward = -1; }
        if (event.code === 'KeyD') { keyStates.D = true; playerActionState.turn = 1 }
        if (event.code === 'Space') { keyStates.Space = true; }
    });
    // 当某个键盘抬起设置对应属性设置为false
    document.addEventListener('keyup', (event: KeyboardEvent) => {
        if (event.code === 'KeyW') {
            keyW = false
            keyStates.W = false; playerActionState.forward = 0;
            ForwardHoldTimeClock.stop()
            ForwardHoldTimeClock.elapsedTime = 0
        }
        if (event.code === 'KeyA') { keyStates.A = false; playerActionState.turn = 0 }
        if (event.code === 'KeyS') { keyStates.S = false; playerActionState.forward = 0 }
        if (event.code === 'KeyD') { keyStates.D = false; playerActionState.turn = 0 }
        if (event.code === 'Space') keyStates.Space = false

        // 保持按键打断前的状态
        playerActionState.forward = keyStates.W == true ? 1 : playerActionState.forward
        playerActionState.turn = keyStates.A == true ? -1 : playerActionState.turn
        playerActionState.forward = keyStates.S == true ? -1 : playerActionState.forward
        playerActionState.turn = keyStates.D == true ? 1 : playerActionState.turn
    });
    // 鼠标按下时锁定禁用鼠标指针
    that.container.addEventListener('mousedown', (e: MouseEvent) => {
        obj.look ? document.body.requestPointerLock() : document.exitPointerLock()
        if (e.button == 0) {
            // 鼠标左键被点击
            keyStates.leftMouseBtn = true
        }
        mouseTime = performance.now()
    })
    // 鼠标按键抬起
    document.addEventListener('mouseup', (e: MouseEvent) => {
        if (document.pointerLockElement !== null) throwBall();
        if (e.button == 0) {
            // 鼠标左键抬起
            keyStates.leftMouseBtn = false
        }
    })
    // 相机视角跟随鼠标旋转
    document.body.addEventListener('mousemove', (event: MouseEvent) => {
        if (document.pointerLockElement === document.body) {
            that.player.rotation.y -= event.movementX / (cameraMoveSensitivity * 1000);
            // that.camera.rotation.x -= event.movementY / (cameraMoveSensitivity * 1000);
        } else if (keyStates.leftMouseBtn) {
            // 鼠标左键拖动时移动视角
            if (cameraMoveSensitivity <= 0) cameraMoveSensitivity = 0.001
            if (cameraMoveSensitivity > 1) cameraMoveSensitivity = 1
            that.player.rotation.y -= event.movementX / (cameraMoveSensitivity * 1000);
            that.camera.rotation.x -= event.movementY / (cameraMoveSensitivity * 1000);
        }
    });
    gui.add(obj, "look").name("是否锁定鼠标").onChange((value) => {
        obj.look = value
    })
    const loader = new GLTFLoader().setPath('/models/')
    const dracoLoader = new DRACOLoader()
    dracoLoader.setDecoderPath('/draco/')
    loader.setDRACOLoader(dracoLoader)
    // 比奇堡
    loader.load("bfbb_glb/source/bikinibottom.glb", (gltf) => {
        gltf.scene.position.set(-10, -10, 0)
        gltf.scene.rotation.y = -Math.PI / 2;
        that.scene.add(gltf.scene);
        // gltf.scene.getObjectByName("碰撞检测").visible = false
        worldOctree.fromGraphNode(gltf.scene);
        // 修改材质，启用阴影
        gltf.scene.traverse(child => {
            if (child instanceof THREE.Mesh) {
                child.castShadow = true;
                child.receiveShadow = true;
                if (child.material.map) {
                    child.material.map.anisotropy = 4;
                }
            }
        });
        const helper = new OctreeHelper(worldOctree);
        helper.visible = false;
        that.scene.add(helper);
        gui.add({ OctreeDebug: false }, 'OctreeDebug')
            .onChange(function (value) {
                helper.visible = value;
            });
    });
    // 宝箱
    loader.load('bx.glb', (gltf) => {
        gltf.scene.scale.set(0.5, 0.5, 0.5)
        gltf.scene.rotation.y = 28.6
        gltf.scene.position.set(1.03, -5, 2.87)
        worldOctree.fromGraphNode(gltf.scene);
        that.scene.add(gltf.scene)
        //包含关键帧动画的模型作为参数创建一个播放器
        const mixer = new THREE.AnimationMixer(gltf.scene);
        //  获取gltf.animations[0]的第一个clip动画对象
        const clipAction = mixer.clipAction(gltf.animations[0]); //创建动画clipAction对象
        clipAction.play(); //播放动画
        const clock = new THREE.Clock();
        function loop() {
            requestAnimationFrame(loop);
            //clock.getDelta()方法获得loop()两次执行时间间隔
            const frameT = clock.getDelta();
            // 更新播放器相关的时间
            mixer.update(frameT);
        }
        loop();
        //不循环播放
        clipAction.loop = THREE.LoopOnce;
        // 物体状态停留在动画结束的时候
        clipAction.clampWhenFinished = true
    })
    // 角色模型
    loader.load("fish.glb", (gltf) => {
        that.player = gltf.scene
        that.scene.add(that.player)
        that.player.add(that.camera)
        that.player.add(playerColliderHelper)
        playerColliderHelper.visible = false
        // 启用阴影
        that.player.traverse(function (object: THREE.Mesh) {
            if (object.isMesh) object.castShadow = true;
        });
        // 关键帧动画
        const animations = gltf.animations;
        that.mixer = new THREE.AnimationMixer(that.player);
        that.allActions = {}
        for (let i = 0; i < animations.length; i++) {
            // agree,headShake,Breathing Idle,Standard Run,sad_pose,sneak_pose,Walking
            const clip = animations[i];//休息、步行、跑步等动画的clip数据
            const action = that.mixer.clipAction(clip);//clip生成action
            action.name = clip.name;//action命名name
            // 批量设置所有动画动作的权重
            if (action.name === 'Breathing Idle') {
                action.weight = 1.0;//默认播放Breathing Idle对应的休息动画
            } else {
                action.weight = 0.0;
            }
            action.play();
            // action动画动作名字作为actionObj的属性
            that.allActions[action.name] = action;
        }
        that.currentAction = that.allActions['Breathing Idle']
    })
    // boss
    loader.load('bot_boss/scene.gltf', (gltf) => {
        gltf.scene.scale.set(0.08, 0.08, 0.08)
        gltf.scene.position.set(28, -10, -75)
        gltf.scene.rotateY(5)
        worldOctree.fromGraphNode(gltf.scene);
        that.scene.add(gltf.scene)
        const mixer = new THREE.AnimationMixer(gltf.scene);
        const clipAction = mixer.clipAction(gltf.animations[5]);
        clipAction.play(); //播放动画
        const clock = new THREE.Clock();
        function loop() {
            requestAnimationFrame(loop);
            const frameT = clock.getDelta();
            mixer.update(frameT);
        }
        loop();
    })


    gui.add({ colliderHelper: false }, 'colliderHelper')
        .name("胶囊护体")
        .onChange(function (value) {
            playerColliderHelper.visible = value
        });
}